﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Skybot.Net.WebSockets.Sockets
{
    /// <summary>
    /// 客户端发送数据解析
    /// </summary>
    public class ClientFrameParse
    {
        #region 解析接收到的数据相关
        /// <summary>
        /// 获取数据贴
        /// </summary>
        /// <param name="conn">当前联接对像</param>
        /// <param name="connState">对应联接对像的状态对像</param>
        /// <returns>如果找到有效挣则返回数据</returns>
        public byte[] GetFrameData(SocketClientConnection conn, ReadState connState)
        {
            //需要返回的数据
            byte[] frameData = null;
            var ReceiveData = connState.ReceiveData;
            //基于当前位置的偏移量
            int offset = 0;
            //当数据项大于2时才有效
            if (ReceiveData.Count > 2)
            {
                byte bt1 = connState.ReceiveData[offset];
                byte bt2 = connState.ReceiveData[offset + 1];

                offset += 2;

                #region 解析说明
                //Console.WriteLine(string.Format("FIN:{0},RSV1:{1} RSV2:{2} RSV3:{3};Opcode:{4},mask:{5};len:{6}"
                //    , bt1 >> 7 & 1
                //    , (bt1 >> 6) & 1
                //    , (bt1 >> 5) & 1
                //    , (bt1 >> 4) & 1
                //    , (bt1 & 0xF).ToString("X2")
                //    ///////第二字节
                //    , bt2 >> 7 & 1 //mask
                //    , bt2 & 0x7f //Payload len   ==((byte)(bt2<<1)>>1)
                //    ));
                //FIN：1位，用来表明这是一个消息的最后的消息片断，当然第一个消息片断也可能是最后的一个消息片断
                //RSV1, RSV2, RSV3: 分别都是1位，如果双方之间没有约定自定义协议，那么这几位的值都必须为0,否则必须断掉WebSocket连接
                //Opcode:4位操作码，定义有效负载数据，如果收到了一个未知的操作码，连接也必须断掉，以下是定义的操作码：
                //*  %x0 表示连续消息片断
                //*  %x1 表示文本消息片断
                //*  %x2 表未二进制消息片断
                //*  %x3-7 为将来的非控制消息片断保留的操作码
                //*  %x8 表示连接关闭
                //*  %x9 表示心跳检查的ping
                //*  %xA 表示心跳检查的pong
                //*  %xB-F 为将来的控制消息片断的保留操作码
                #endregion


                //操作码
                int Opcode = (bt1 & 0xF);
                bool fin = (bt1 >> 7 & 1) == 1;
                int mask = (bt2 & 0x80) >> 7;
                int payloadLen = bt2 & 0x7f;

                //心跳什么的直接删除

                //其它数据
                switch (Opcode)
                {
                    //*  %x0 表示连续消息片断
                    case 0:
                        //不做任何处理
                        frameData = GetFrameBytes(ReceiveData, connState, mask, fin, payloadLen, ref offset);
                        break;
                    //*  %x1 表示文本消息片断
                    case 1:
                        frameData = GetFrameBytes(ReceiveData, connState, mask, fin, payloadLen, ref offset);
                        break;
                    //*  %x2 表未二进制消息片断
                    case 2:
                        frameData = GetFrameBytes(ReceiveData, connState, mask, fin, payloadLen, ref offset);
                        break;
                    //*  %x3-7 为将来的非控制消息片断保留的操作码
                    //case 3-7:
                    //    break;
                    //*  %x8 表示连接关闭
                    case 8:
                        try
                        {
                            conn.Socket.Close();
                        }
                        catch (System.Net.Sockets.SocketException) { }
                        catch (ObjectDisposedException) { }
                        catch (NullReferenceException) { }

                        break;
                    //*  %x9 表示心跳检查的ping
                    case 9:
                        if (mask == 1) { payloadLen = 6; }
                        byte[] heartbeat = ReceiveData.GetRange(0, payloadLen).ToArray();
                        //删除数据
                        int curoffset = offset - 2;
                        //直接删除数据
                        ReceiveData.RemoveRange(curoffset, payloadLen);
                        Console.WriteLine(string.Format("心跳数据ping删除:{0}", BitConverter.ToString(heartbeat)));

                        break;
                    //*  %xA 表示心跳检查的pong
                    case 0xA:
                        if (mask == 1) { payloadLen = 6; }
                        byte[] heartbeatPong = ReceiveData.GetRange(0, payloadLen).ToArray();
                        //删除数据
                        int curoff = offset - 2;
                        //直接删除数据
                        ReceiveData.RemoveRange(curoff, payloadLen);
                        Console.WriteLine(string.Format("心跳数据pong删除:{0}", BitConverter.ToString(heartbeatPong)));
                        break;
                    //*  %xB-F 为将来的控制消息片断的保留操作码
                    default:
                        try
                        {

                            byte[] def = ReceiveData.GetRange(0, payloadLen).ToArray();
                            Console.WriteLine(string.Format("未知数据" + Opcode.ToString("X2") + "删除:{0}", BitConverter.ToString(def)));
                        }
                        catch
                        {
                        }
                        //直接删除数据
                        ReceiveData.RemoveRange(0, payloadLen + offset);

                        break;

                }

            }

            return frameData;
        }

        /// <summary>
        /// 获取一包数据,用于处理不同类型
        /// </summary>
        /// <param name="ReceiveData">联接接收数据的缓冲区..</param>
        /// <param name="connState">用于保存单个有效协议帧的对像</param>
        /// <param name="mask">掩码值1表示有</param>
        /// <param name="fin">是否最后一包数据1表示最后一包</param>
        /// <param name="payloadLen"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        protected byte[] GetFrameBytes(List<byte> ReceiveData, ReadState connState, int mask, bool fin, int payloadLen, ref int offset)
        {
            long ProtocolLen = payloadLen;
            //没有数据直接返回
            if (ProtocolLen < 1)
            {
                //直接删除数据,同时也需要 删除掩码
                ReceiveData.RemoveRange(0, payloadLen + offset + (mask == 1 ? 4 : 0));
                return null;
            }
            //如果有掩码先获取掩码
            byte[] marskBytes = null;

            //如果消息小于126
            if (payloadLen < 126)
            {

                //获取掩码
                if (mask == 1)
                {
                    marskBytes = ReceiveData.Skip(offset).Take(4).ToArray();
                    offset += 4;
                }
                //刚好有一帧的数据
                if (ReceiveData.Count >= payloadLen + 2)
                {
                    byte[] data = null;
                    if (marskBytes == null)
                    {
                        data = ReceiveData.GetRange(offset, payloadLen).ToArray();
                    }
                    else
                    {
                        data = ReceiveData.GetRange(offset, payloadLen)
                            .Select((p, i) => (byte)(p ^ marskBytes[i % 4])).ToArray();

                    }
                    //直接删除数据
                    ReceiveData.RemoveRange(0, payloadLen + offset);


                    connState.Data.AddRange(data);
                    if (fin)
                    {

                        byte[] tmpdata = connState.Data.ToArray();
                        connState.Data.Clear();
                        return tmpdata;

                    }
                }



            }
            else if (payloadLen == 126)
            {
                //大端数据长度
                byte[] lenbytes = new byte[2];
                lenbytes[0] = ReceiveData[offset + 1 + (marskBytes != null ? -marskBytes.Length : 0)];
                lenbytes[1] = ReceiveData[offset + 0 + (marskBytes != null ? -marskBytes.Length : 0)];
                ProtocolLen = BitConverter.ToUInt16(lenbytes, 0);
                offset += 2;
                //获取掩码
                if (mask == 1)
                {
                    marskBytes = ReceiveData.Skip(offset).Take(4).ToArray();
                    offset += 4;
                }

                //刚好有一帧的数据
                if (ReceiveData.Count >= ProtocolLen + 2)
                {
                    byte[] data = null;
                    if (marskBytes == null)
                    {

                        data = ReceiveData.GetRange(offset, (int)ProtocolLen).ToArray();
                    }
                    else
                    {
                        data = ReceiveData.GetRange(offset, (int)ProtocolLen)
                              .Select((p, i) => (byte)(p ^ marskBytes[i % 4])).ToArray();
                    }

                    //直接删除数据
                    ReceiveData.RemoveRange(0, (int)ProtocolLen + offset);


                    connState.Data.AddRange(data);
                    if (fin)
                    {

                        byte[] tmpdata = connState.Data.ToArray();
                        connState.Data.Clear();
                        return tmpdata;

                    }
                }
            }
            else if (payloadLen == 127)
            {
                //大端数据长度
                byte[] lenbytes = new byte[8];
                lenbytes[0] = ReceiveData[offset + 7 + (marskBytes != null ? -marskBytes.Length : 0)];
                lenbytes[1] = ReceiveData[offset + 6 + (marskBytes != null ? -marskBytes.Length : 0)];
                lenbytes[2] = ReceiveData[offset + 5 + (marskBytes != null ? -marskBytes.Length : 0)];
                lenbytes[3] = ReceiveData[offset + 4 + (marskBytes != null ? -marskBytes.Length : 0)];
                lenbytes[4] = ReceiveData[offset + 3 + (marskBytes != null ? -marskBytes.Length : 0)];
                lenbytes[5] = ReceiveData[offset + 2 + (marskBytes != null ? -marskBytes.Length : 0)];
                lenbytes[6] = ReceiveData[offset + 1 + (marskBytes != null ? -marskBytes.Length : 0)];
                lenbytes[7] = ReceiveData[offset];
                ProtocolLen = BitConverter.ToInt64(lenbytes, 0);
                offset += 8;
                //获取掩码
                if (mask == 1)
                {
                    marskBytes = ReceiveData.Skip(offset).Take(4).ToArray();
                    offset += 4;
                }
                //刚好有一帧的数据
                if (ReceiveData.Count >= ProtocolLen + 2)
                {
                    byte[] data = null;
                    if (marskBytes == null)
                    {

                        data = ReceiveData.GetRange(offset, (int)ProtocolLen).ToArray();

                    }
                    else
                    {

                        data = ReceiveData.GetRange(offset, (int)ProtocolLen)
                              .Select((p, i) => (byte)(p ^ marskBytes[i % 4])).ToArray();
                    }
                    //直接删除数据
                    ReceiveData.RemoveRange(0, (int)ProtocolLen + offset);


                    connState.Data.AddRange(data);
                    if (fin)
                    {

                        byte[] tmpdata = connState.Data.ToArray();
                        connState.Data.Clear();
                        return tmpdata;

                    }

                }
            }
            return null;
        }




        #endregion
    }
}
